#include <iostream>
#include "float.h"

#include "ddas_debug.h"
#include "ddas_results.h"
#include "ddas_globals.h"

#include "lip.h"

#include "matrix/gsl_matrix.h"

using namespace std;


//START RESULT MANIPULATION


/** 
* @author Max Rupplin
* @date 5-15-08
* @param result_set The result_set through which to search
*
* 'ddas_get_ptr_to_worst_result' returns a pointer to the worst result in 'result_set'
*/ 
DDAS_CLIENT_RESULT* ddas_get_ptr_to_worst_result(DDAS_CLIENT_RESULT_SET* result_set)
{
	DDAS_CLIENT_RESULT* retval;

	double current_max=0.0;

	for(int i=0; i<result_set->size; i++)
	{
		DDAS_CLIENT_RESULT tmp=result_set->result[i];

		if( tmp.sse > current_max)
		{
			retval=&(result_set->result[i]);
			current_max=tmp.sse;
		}
	}

	return retval;
}


/** 
* @author Max Rupplin
* @date 5-15-08
* @param result_set The result_set through which to search
*
* 'ddas_get_index_of_max_SSE' returns an index of worst (maximum) SSE in 'result_set'
*/ 
int ddas_get_index_of_max_SSE(DDAS_CLIENT_RESULT_SET* result_set)
{
	double current_max=0.0;
	int current_max_index;

	for(int i=0; i<result_set->size; i++)
	{
		DDAS_CLIENT_RESULT current=result_set->result[i];

		if(current.sse>current_max)
		{
			current_max_index=i;
			current_max=current.sse;
		}
	}

	return current_max_index;
}


/** 
* @author Max Rupplin
* @date 5-15-08
* @param result_set The result_set through which to search
*
* 'ddas_get_max_SSE' returns the maximum SSE in 'result_set'
*/ 
double ddas_get_max_SSE(DDAS_CLIENT_RESULT_SET* result_set)
{
	#ifdef DEBUG_MODE
	cerr<<"ddas_get_max_SSE called."<<endl;
	#endif

	double current_max=0;

	for(int i=0; i<result_set->size; i++)
	{
		DDAS_CLIENT_RESULT current=result_set->result[i];

		if(&current==NULL)return NULL;
		if(current.sse>current_max) current_max=current.sse;
	}

	#ifdef DEBUG_MODE
	cerr<<"ddas_get_max_SSE returning."<<endl;
	#endif

	return current_max;
}


/** 
* @author Max Rupplin
* @date 5-15-08
* @param result_set The result_set through which to search
*
* 'ddas_replace_worst_result' replaces the worst result in the DDAS_CLIENT_RESULT_SET with 'result', checking first if 'result' < 'worst'
*/ 
void ddas_replace_worst_result(DDAS_CLIENT_RESULT* result)
{
	#ifdef DEBUG_MODE
	cerr<<"ddas_replace_worst_result called."<<endl;
	#endif

	//sanity check - is new_result's SSE really better/lower?
	if( ddas_get_max_SSE(result_set) > (result->sse) )
	{
		//get current worst result (based on SSE)
		DDAS_CLIENT_RESULT* current_worst_result=ddas_get_ptr_to_worst_result(result_set);
		
		*(current_worst_result)=*(result);
		
		#ifdef DEBUG_MODE
		ddas_print_client_result(current_worst_result);		
		#endif
	}

	#ifdef DEBUG_MODE
	cerr<<"ddas_replace_worst_result returning."<<endl;
	#endif
}

//END RESULT MANIPULATION



//CLIENT RESULT ALLOCATION


/** 
* @author Max Rupplin
* @date 5-15-08
* @param iteration The iteration from which this result was derived
* @param regression_coefficients The associated coefficients with this DDAS_CLIENT_RESULT
* @param SSE The sum of squared error (chi^2) for this DDAS_CLIENT_RESULT
*
* 'ddas_client_result_alloc' mallocs a single DDAS_CLIENT_RESULT and return a pointer to it
*/ 
DDAS_CLIENT_RESULT* ddas_client_result_alloc(verylong iteration, gsl_vector* regression_coefficients, double SSE)
{
	#ifdef DEBUG_MODE
	cerr<<"ddas_client_result_alloc called."<<endl;
	#endif

	DDAS_CLIENT_RESULT* result = new DDAS_CLIENT_RESULT;

	result->definition_number = 0;
	zcopy( iteration, &(result->definition_number) );

	result->regression_coefficients = gsl_vector_alloc(regression_coefficients->size);
	gsl_vector_memcpy (result->regression_coefficients, regression_coefficients);

	result->sse=0;
	result->sse=SSE;

	#ifdef DEBUG_MODE
	cerr<<"ddas_client_result_alloc returning."<<endl;
	#endif

	return result;
}


/** 
* @author Max Rupplin
* @date 5-15-08
*
* 'ddas_client_result_calloc' mallocs then zero struct member data
*/ 
DDAS_CLIENT_RESULT* ddas_client_result_calloc()
{
	DDAS_CLIENT_RESULT* result=(DDAS_CLIENT_RESULT*)malloc(sizeof(DDAS_CLIENT_RESULT));

	result->definition_number=0;
	result->regression_coefficients=NULL;
	result->sse=0;

	return result;
}


//END CLIENT RESULT ALLOCATION




//START CLIENT RESULT SET ALLOCATION


/** 
* @author Max Rupplin
* @date 5-15-08
* @param size The number of results to be included in this DDAS_CLIENT_RESULT_SET
*
* 'ddas_client_result_set_alloc' allocs a DDAS_CLIENT_RESULT_SET of size 'size'
*/ 
DDAS_CLIENT_RESULT_SET* ddas_client_result_set_alloc(int size)
{ 
	#ifdef DEBUG_MODE
	cerr<<"ddas_client_result_set_alloc called."<<endl;
	#endif

	//DDAS_CLIENT_RESULT_SET* result_set=(DDAS_CLIENT_RESULT_SET*)malloc((sizeof(DDAS_CLIENT_RESULT)*size) + sizeof(int));
	DDAS_CLIENT_RESULT_SET* result_set = new DDAS_CLIENT_RESULT_SET;

	result_set->size = size;
	result_set->result = new DDAS_CLIENT_RESULT[size];

	//initialize SSEs to highest (worst) possible values
	for(int i=0; i<size; i++)
	{
		(result_set->result[i]).sse=DBL_MAX;
	}

	#ifdef DEBUG_MODE
	cerr<<"Allocated "<<sizeof(result_set)<<" bytes for result_set of size "<<size<<"."<<endl;
	cerr<<"ddas_client_result_set_alloc returning."<<endl;
	#endif

	return result_set;
}


/** 
* @author Max Rupplin
* @date 5-15-08
* @param size The number of results to be included in this DDAS_CLIENT_RESULT_SET
*
* 'ddas_client_result_set_calloc' callocs (zeros data) a DDAS_CLIENT_RESULT_SET of size 'size'
*/ 
DDAS_CLIENT_RESULT_SET* ddas_client_result_set_calloc(int size)
{
	#ifdef DEBUG_MODE
	cerr<<"ddas_client_result_set_calloc called."<<endl;	
	#endif

	DDAS_CLIENT_RESULT_SET* result_set = new DDAS_CLIENT_RESULT_SET;

	for(int i=0; i<result_set->size; i++)
	{
		result_set->result[i]=*ddas_client_result_calloc();
	}

	#ifdef DEBUG_MODE
	cerr<<"ddas_client_result_set_calloc returning."<<endl;	
	#endif

	return result_set;
}

//END ALLOCATION


/** 
* @author Max Rupplin
* @date 5-15-08
* @param result The result to deallocate from memory
*
* 'ddas_client_result_free' attempts to free a DDAS_CLIENT_RESULT
*/ 
void ddas_client_result_free(DDAS_CLIENT_RESULT* result)
{
	try
	{
		free(result);
	}
	catch(void*)
	{
		cerr<<"Error freeing DDAS_CLIENT_RESULT #";
		zwrite(result->definition_number);
		cerr<<"."<<endl;
	}
}


/** 
* @author Max Rupplin
* @date 5-15-08
* @param result_set The DDAS_CLIENT_RESULT_SET to free
*
* 'ddas_client_result_set_free' attempts to free a DDAS_CLIENT_RESULT_SET
*/ 
void ddas_client_result_set_free(DDAS_CLIENT_RESULT_SET* result_set)
{
	#ifdef DEBUG_MODE
	cerr<<"ddas_client_result_set_free called."<<endl;
	#endif

	try
	{
		free(result_set);
	}
	catch(void*)
	{
		cerr<<"Error freeing DDAS_CLIENT_RESULT_SET."<<endl;
	}

	#ifdef DEBUG_MODE
	cerr<<"ddas_client_result_set_free returning."<<endl;
	#endif
}